package pe.com.ValoracionVivienda.views.entrenamiento;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityTransaction;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.wb.swt.SWTResourceManager;

import pe.com.ValoracionVivienda.container.AppContainer;
import pe.com.ValoracionVivienda.domain.entities.ArquitecturaRNA;
import pe.com.ValoracionVivienda.domain.entities.Entrenamiento;
import pe.com.ValoracionVivienda.domain.entities.EntrenamientoItemEntrada;
import pe.com.ValoracionVivienda.domain.entities.EntrenamientoModelo;
import pe.com.ValoracionVivienda.domain.entities.TipoTasacion;
import pe.com.ValoracionVivienda.domain.entities.Usuario;
import pe.com.ValoracionVivienda.domain.tipos.TipoValoracion;
import pe.com.ValoracionVivienda.service.impl.ArquitecturaRNAServiceJpaImpl;
import pe.com.ValoracionVivienda.service.impl.EntrenamientoServiceJpaImpl;
import pe.com.ValoracionVivienda.service.interfaces.ArquitecturaRNAService;
import pe.com.ValoracionVivienda.service.interfaces.EntrenamientoService;
import pe.com.ValoracionVivienda.views.resource.shared.SP3ResourceShared;
import weka.classifiers.functions.MultilayerPerceptron;
import weka.core.Attribute;
import weka.core.Instances;

public class ShellEntrenamientoDetalle extends Shell {

	private Integer idEntrenamiento;
	private TipoTasacion tipoTasacion;
	private TipoValoracion tipoValoracion;
	private boolean huboCambios;
	private ComboViewer comboViewer;
	private Text txtNombre;
	private String rutaArchivoDatos;
	private Label lblRutaData;
	private ArquitecturaRNA arquitecturaRNA;
	
	public void initData(Integer idTipoTasacion, TipoValoracion tipoValoracion){
		if (idTipoTasacion == null)
			tipoTasacion = null;
		else {
			tipoTasacion = new TipoTasacion();
			tipoTasacion.setId(idTipoTasacion);
		}
			
		this.tipoValoracion = tipoValoracion;
		LoadCombo();
	}
	
	private void LoadCombo() {
		ArquitecturaRNAService arquitecturaRNAService = new ArquitecturaRNAServiceJpaImpl();
		ArquitecturaRNA[] arquitecturasRNA = arquitecturaRNAService.getActivos().toArray(new ArquitecturaRNA[0]);
		comboViewer.setInput(arquitecturasRNA);
	}

	/**
	 * Create the shell.
	 * @param display
	 */
	public ShellEntrenamientoDetalle(Display display) {
		super(display, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
		
		Label lblArquitecturaRna = new Label(this, SWT.NONE);
		lblArquitecturaRna.setBounds(20, 21, 93, 15);
		lblArquitecturaRna.setText("Arquitectura RNA");
		
		Label lblNombre = new Label(this, SWT.NONE);
		lblNombre.setBounds(20, 50, 44, 15);
		lblNombre.setText("Nombre");
		
		Label lblData = new Label(this, SWT.NONE);
		lblData.setBounds(20, 83, 24, 15);
		lblData.setText("Data");
		
		Button btnAceptar = new Button(this, SWT.NONE);
		btnAceptar.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				aceptar();
			}
		});
		btnAceptar.setBounds(236, 166, 75, 25);
		btnAceptar.setText("Aceptar");
		
		Button btnCancelar = new Button(this, SWT.NONE);
		btnCancelar.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				cancelar();
			}
		});
		btnCancelar.setBounds(340, 166, 75, 25);
		btnCancelar.setText("Cancelar");
		
		Combo cboArquitecturas = new Combo(this, SWT.READ_ONLY);
		cboArquitecturas.setBounds(142, 18, 270, 23);
		comboViewer = new ComboViewer(cboArquitecturas);
		
		txtNombre = new Text(this, SWT.BORDER);
		txtNombre.setBounds(142, 47, 270, 21);
		
		Button btnCargarData = new Button(this, SWT.NONE);
		btnCargarData.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				cargarData();
			}
		});
		btnCargarData.setBounds(142, 78, 75, 25);
		btnCargarData.setText("Cargar Data");
		
		lblRutaData = new Label(this, SWT.WRAP);
		lblRutaData.setForeground(SWTResourceManager.getColor(SWT.COLOR_LIST_SELECTION));
		lblRutaData.setText("NO DATA");
		lblRutaData.setBounds(142, 110, 270, 50);
		
		comboViewer.setContentProvider(new IStructuredContentProvider() {
			@Override
			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
				System.out.println("Input changed: old=" + oldInput + ", new=" + newInput);
			}
			
			@Override
			public void dispose() {
				System.out.println("Disposing... ComboViewer");
			}
			
			@Override
			public Object[] getElements(Object inputElement) {
				ArquitecturaRNA[] arquitecturasRNA = (ArquitecturaRNA[]) inputElement;
				return arquitecturasRNA;
			}
		});
		
		comboViewer.setLabelProvider(new LabelProvider(){
			@Override
			public String getText(Object element){
				return ((ArquitecturaRNA)element).getNombre();
			}
		});
		
		createContents();
		centrarEnPantalla(display);
	}

	protected void cargarData() {
		FileDialog fileDialog = new FileDialog(this, SWT.OPEN);
		//fileDialog.setFilterPath(System.getProperty("user.home"));
		fileDialog.setFilterPath(System.getenv("SystemDrive"));
		fileDialog.setFilterExtensions(new String[]{"*.arff"});
		fileDialog.setFilterNames(new String[]{"Archivos arff (*.arff)"});
		rutaArchivoDatos = fileDialog.open();
		if (rutaArchivoDatos != null){
			lblRutaData.setText("Archivo cargado: " + rutaArchivoDatos);
		}
	}

	protected void cancelar() {
		this.dispose();
	}

	protected void aceptar() {
		if (!validarInputs()){
			MessageBox messageBox = new MessageBox(this.getShell(), SWT.ICON_WARNING | SWT.OK);
			messageBox.setText(SP3ResourceShared.getString("SP3"));
			messageBox.setMessage(SP3ResourceShared.getString("DebeIngresarTodosCampos"));
			messageBox.open();
			return ;
		} else if(!validarDataCargada()) {
			MessageBox messageBox = new MessageBox(this.getShell(), SWT.ICON_WARNING | SWT.OK);
			messageBox.setText(SP3ResourceShared.getString("SP3"));
			messageBox.setMessage(SP3ResourceShared.getString("DebeCargarDataEntrenamiento"));
			messageBox.open();
			return ;
		} else {
			crearEntrenamiento();
		}
	}

	private void crearEntrenamiento() {
		BufferedReader br = null;
		Instances data; 	// weka
		
		// 1. Crear entrenamiento
		Entrenamiento entrenamiento = new Entrenamiento();
		EntrenamientoModelo entrenamientoModelo = null;
		
		entrenamiento.setTipoTasacion(tipoTasacion);
		entrenamiento.setArquitecturaRNA(arquitecturaRNA);
		
		entrenamiento.setNombre(txtNombre.getText());
		entrenamiento.setEstado(true);
		entrenamiento.setTipo((short)tipoValoracion.getValor());
		entrenamiento.setFechaCreacion(new Date());
		entrenamiento.setUsuarioCreacion(new Usuario(AppContainer.ID_USUARIO));
		
		// 1. y 2. Crear EntrenamientoModelo y 3. Crear Items Capa Entrada
		// weka
		try {
			InputStream is = new FileInputStream(new File(rutaArchivoDatos));
			br = new BufferedReader(new FileReader(rutaArchivoDatos));
			data = new Instances(br);
			data.setClassIndex(data.numAttributes() - 1);
			entrenamiento.setCantidadNeuronasEntrada(data.numAttributes() - 1);
			entrenamientoModelo = new EntrenamientoModelo();
			entrenamientoModelo.setDataEntrenamiento(IOUtils.toByteArray(is));
			
			Set<EntrenamientoItemEntrada> itemsEntrada = 
					new HashSet<EntrenamientoItemEntrada>(data.numAttributes());
			
			for (int i = 0; i < data.numAttributes() - 1; i++) {
				Attribute attr = data.attribute(i);
				EntrenamientoItemEntrada entrenamientoItemEntrada = new EntrenamientoItemEntrada();
				entrenamientoItemEntrada.setEntrenamiento(entrenamiento);
				entrenamientoItemEntrada.setNombre(attr.name());
				entrenamientoItemEntrada.setOrden(i+1);
				itemsEntrada.add(entrenamientoItemEntrada);
			}
			entrenamiento.setItemsCapaEntradaEntrenamiento(itemsEntrada);
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
			MessageBox messageBox = new MessageBox(this.getShell(), SWT.ICON_WARNING | SWT.OK);
			messageBox.setText(SP3ResourceShared.getString("SP3"));
			messageBox.setMessage(SP3ResourceShared.getString("NoPudoCargarArchivoDATA"));
			messageBox.open();
			return ;
		}
		
		
		// 3. Empezar entrenamiento
		// weka
		MultilayerPerceptron multilayerPerceptron = new MultilayerPerceptron();
		multilayerPerceptron.setLearningRate(arquitecturaRNA.getLearningRate());
		multilayerPerceptron.setMomentum(arquitecturaRNA.getMomentum());
		multilayerPerceptron.setTrainingTime(arquitecturaRNA.getTrainingTime());
		multilayerPerceptron.setSeed(0);
		multilayerPerceptron.setValidationSetSize(0);
		multilayerPerceptron.setValidationThreshold(20);
		multilayerPerceptron.setHiddenLayers(arquitecturaRNA.getCapaOculta());
		//multilayerPerceptron.setReset(false);
		multilayerPerceptron.setReset(true);
		try {
			multilayerPerceptron.buildClassifier(data);
			entrenamientoModelo.setModelo(SerializationUtils.serialize(multilayerPerceptron));
			entrenamiento.addModelo(entrenamientoModelo);
			entrenamientoModelo.setEntrenamiento(entrenamiento);
		} catch (Exception e) {
			e.printStackTrace();
			MessageBox messageBox = new MessageBox(this.getShell(), SWT.ICON_WARNING | SWT.OK);
			messageBox.setText(SP3ResourceShared.getString("SP3"));
			messageBox.setMessage(SP3ResourceShared.getString("NoPudoEntrenarRedApropiadamente"));
			messageBox.open();
			return ;
		}
		
		
		// 4. Guardar en bd
		EntityTransaction tran = null;
		EntrenamientoService entrenamientoService = new EntrenamientoServiceJpaImpl();
		
		try {
			tran = entrenamientoService.getTransaction();
			tran.begin();
			entrenamiento = entrenamientoService.persist(entrenamiento);
			tran.commit();
			idEntrenamiento = entrenamiento.getId();
			huboCambios = true;
			this.close();
		} catch (Exception e) {
			tran.rollback();
		} finally {
			entrenamientoService.close();
        }
	}

	private boolean validarDataCargada() {
		boolean isValido = true;
		
		if (rutaArchivoDatos == null)
			isValido = false;
		return isValido;
	}

	private boolean validarInputs() {
		boolean isValido = true;
		
		IStructuredSelection selection = (IStructuredSelection)comboViewer.getSelection();
		arquitecturaRNA = (ArquitecturaRNA)selection.getFirstElement();
		
		if (txtNombre.getText().isEmpty() || arquitecturaRNA == null)
			isValido = false;
		return isValido;
	}

	/**
	 * Create contents of the shell.
	 */
	protected void createContents() {
		setText("Entrenamiento");
		setSize(431, 230);

	}
	
	private void centrarEnPantalla(Display display){
		Monitor primary = display.getPrimaryMonitor();
	    Rectangle bounds = primary.getBounds();
	    Rectangle rect = getBounds();
	    
	    int x = bounds.x + (bounds.width - rect.width) / 2;
	    int y = bounds.y + (bounds.height - rect.height) / 2;
	    setLocation(x, y);
	}

	public boolean getHuboCambios(){ return huboCambios; }
	
	public Integer getIdEntrenamiento() { return idEntrenamiento; }
	
	@Override
	protected void checkSubclass() {
		// Disable the check that prevents subclassing of SWT components
	}
}
